#include"AirParticle.h"


quadRect* quadRect::createWithSprite(Sprite* sprite){
	quadRect* rect = new quadRect();
	rect->right = sprite->getBoundingBox().size.width/2+sprite->getPosition().x;
	rect->top = sprite->getBoundingBox().size.height/2+sprite->getPosition().y;
	rect->left = -sprite->getBoundingBox().size.width/2+sprite->getPosition().x;
	rect->bottom = -sprite->getBoundingBox().size.height/2+sprite->getPosition().y;
	return  rect;
}



AirParticle* AirParticle::create(Air* air,Texture2D *texture,Point speed, float changeSize,float life,float startOpacity,float endOpacity){
    AirParticle *sprite = new (std::nothrow) AirParticle();
    if (sprite && sprite->initWithTexture(texture))
    {
		sprite->setOpacity(startOpacity);
        auto remove = [=](){
            sprite->getParent()->removeChild(sprite);
        };
        CallFunc* call = CallFunc::create(remove);
        sprite->runAction(Sequence::create(Spawn::create(MoveBy::create(life,speed),ScaleBy::create(life,changeSize),FadeTo::create(life,endOpacity),nullptr),call,nullptr));
        sprite->autorelease();
		sprite->_air = air;
		sprite->_rect = quadRect::createWithSprite(sprite);
		sprite->_air->insert(sprite->_rect);
		sprite->checkSchedule();
        return sprite;
    }
    CC_SAFE_DELETE(sprite);
    return nullptr;
}

void AirParticle::checkSchedule(){
	schedule(schedule_selector(AirParticle::updateAir),1/30);
}

void AirParticle::updateAir(float dt){

	_rect->right = getBoundingBox().size.width/2+getPosition().x;
	_rect->top = getBoundingBox().size.height/2+getPosition().y;
	_rect->left = getBoundingBox().size.width/2+getPosition().x;
	_rect->bottom = getBoundingBox().size.height/2+getPosition().y;

	_air->change(_rect);
	auto node = _rect->target;
}

Fire* Fire::create(Sprite* sprite,Air* air){
	Fire* fire = new (std::nothrow) Fire();
	if(fire&&fire->init(sprite,air)){
		fire->autorelease();
		log("in create");
		return fire;
	}
	CC_SAFE_DELETE(fire);
	return nullptr;
}

bool Fire::init(Sprite* sprite,Air* air){
	if(!Node::init()){
		return false;
	}
	_sprite = sprite;
	_air = air;
	_rect = quadRect::createWithSprite(sprite);
	_rect->isStar = true;
	scheduleUpdate();
	this->addChild(_sprite);
	_air->insert(_rect);
	return true;
}

void Fire::update(float dt){
	log("update");
	_rect->right = _sprite->getBoundingBox().size.width/2+getPosition().x;
	_rect->top = _sprite->getBoundingBox().size.height/2+getPosition().y;
	_rect->left = _sprite-> getBoundingBox().size.width/2+getPosition().x;
	_rect->bottom = _sprite->getBoundingBox().size.height/2+getPosition().y;
	log("change");
	_air->change(_rect);
}
void Air::change(quadRect* rect){
	while(!containsBox(rect,rect->target)&&rect->target != root){
		rect->target = rect->target->parent;
	}
	insert(rect,rect->target);
}

bool Air::init(){
    if(!Node::init()){
        return false;
    }
	schedule(schedule_selector(Air::releaseAir),0.05f);

	_speed = _speedVar = 20;
	_size =  2;
	_sizeVar = 0;
	_life = 2;
	_lifeVar = 1;
	_startOpacity = 100;
	_endOpacity = 0;
	_rate = 4;
	_posVar = 20;
	_maxInterval = 40;
	return true;
	
}

void Air::generateTree(){
	root = new QuadNode();
	root->rect.right = Director::getInstance()->getVisibleSize().width;
	root->rect.top = Director::getInstance()->getVisibleSize().height;
	root->rect.left = root->rect.bottom = 0;
	root->depth = 0;
	auto current = root;
	Qqueue.push(current);
	int count = 0;
	while(!Qqueue.empty()){
		current = Qqueue.front();
		Qqueue.pop();
		count++;
			if(current->depth<QUAD_DEPTH){

		for(int i = 0;i<4;++i){
		
		current->order[i] = 0;
		current->list[i] = new QuadNode();
		current->list[i]->depth = current->depth+1;	
			Qqueue.push(current->list[i]);
			current->list[i]->parent = current;
			if(i==0){
				current->list[i]->rect.right = current->rect.right;
				current->list[i]->rect.top = current->rect.top;
				current->list[i]->rect.left = (current->rect.right+current->rect.left)/2;
				current->list[i]->rect.bottom = (current->rect.top+current->rect.bottom)/2;
			}
			if(i==1){
				current->list[i]->rect.right = (current->rect.right+current->rect.left)/2;
				current->list[i]->rect.top = current->rect.top;
				current->list[i]->rect.left = current->rect.left;
				current->list[i]->rect.bottom = (current->rect.top+current->rect.bottom)/2;
			}
			if(i==2){
				current->list[i]->rect.right =  (current->rect.right+current->rect.left)/2;
				current->list[i]->rect.top = (current->rect.top+current->rect.bottom)/2;
				current->list[i]->rect.left = current->rect.left;
				current->list[i]->rect.bottom = current->rect.bottom;
			}
			if(i==3){
				current->list[i]->rect.right = current->rect.right;
				current->list[i]->rect.top = (current->rect.top+current->rect.bottom)/2;
				current->list[i]->rect.left = (current->rect.right+current->rect.left)/2;
				current->list[i]->rect.bottom = current->rect.bottom;
			}
		}
	}
	}
}

void Air::insert(quadRect* rect){
	insert(rect,root);
}

void Air::insert(quadRect *rect,QuadNode* Croot){

	bool find = false;

	auto current = Croot;

	rect->target = Croot;

	if(rect->isStar){
			auto it = rect->target->objs.find(rect);
		if(it != rect->target->objs.end())rect->target->objs.erase(it);
	}

	while(current->depth<QUAD_DEPTH && !find){

		for(int t = 0;t<4;++t){
			
			if(containsBox(rect,current)){
				log("contains");
				current = current->list[t];
				log("t=%d,depth = %d",t,current->depth);
				if(current->depth == QUAD_DEPTH-1){
					find = true;
				current->objs.insert(rect);
				rect->target = current;
				log("edge");
				}
				break;
			}

		}
		if(!find){
			rect->target = current;
			find = true;
		}
	}
	if(rect->isStar){
		rect->target->objs.insert(rect);
		log("height:%f,right:%f,bottom:%f,left:%f",rect->target->rect.top,rect->target->rect.right,rect->target->rect.bottom,rect->target->rect.left);
	}
}

void Air::insertObject(quadRect* rect){
	insert(rect);
}

/*void Air::updateAir(float dt){

	QuadNode* node;
	int currunt = 0;
	node = root;
	Qqueue.push(node);
	while(!Qqueue.empty()){

		checkOut(Qqueue.front());

		Qqueue.pop();

		if(currunt==3){
			currunt=0;
			node = node->list[3];
		}
		else currunt++;

		if(node->order[currunt]==0)continue;
		
		Qqueue.push(node->list[currunt]);

		}	
}
*/

void Air::releaseAir(float dt){
	for(auto air :roadMap){
		if(air.second->start.getDistance(air.second->end) < 2)air.second->stop_to_release = 1;
		else air.second->stop_to_release = 0;
		for(int t = 0;t< air.second->stop_to_release;++t){
			addParticle(air.second->end,0);
		}
		air.second->start = air.second->end;
	}
}

void Air::fill(Point &start, Point &end,Road* road){
	_rate =MAX(0, start.getDistance(end)/ _maxInterval);
	log("%f",_rate);
	for(float i = 0.001f ;i<_rate;++i){
		 addParticle(start+i/_rate*(end-start),0);
	}
	road->end = end;
	road->start = start;
}

void Air::setTouchLayer(Layer* layer){
	

	listenner = EventListenerTouchAllAtOnce::create();
	listenner->onTouchesBegan = [&](const std::vector<Touch*>& touches, Event *unused_event){

		auto target = static_cast<Layer*>(unused_event->getCurrentTarget());
		auto touch = touches[0];
		{
			Road* road = new Road();
			roadMap[touch->getID()] = road;
			auto pos = target->convertToNodeSpace(touch->getLocation());
			roadMap[touch->getID()]->end = pos;
			log("start");
			road->start = road-> end = pos;
		}
	};

	listenner->onTouchesMoved = [&](const std::vector<Touch*>& touches, Event *unused_event){
		auto target = static_cast<Layer*>(unused_event->getCurrentTarget());
		if(touches.size()>0)
		for (auto touch : touches){
			Point currentpos = target->convertToNodeSpace(Director::getInstance()->convertToGL(touch->getLocationInView()));
			Point previouspos = target->convertToNodeSpace(Director::getInstance()->convertToGL(touch->getPreviousLocationInView()));
			float step = currentpos.getDistance(previouspos);
			fill(previouspos,currentpos,roadMap[touch->getID()]);
		}
	};
	listenner->onTouchesEnded = [&](const std::vector<Touch*>& touches, Event *unused_event){
		for (auto touch : touches){
			delete roadMap[touch->getID()];
			roadMap.erase(touch->getID());
		}
	};
	listenner->onTouchesCancelled = [](const std::vector<Touch*>& touches, Event *unused_event){
	};

	_eventDispatcher->addEventListenerWithSceneGraphPriority(listenner, layer);
	_layer = layer;
	//NotificationCenter::getInstance()->addObserver(this, callfuncO_selector(Air::StartRelease), "StartAirRelease",nullptr);
	//NotificationCenter::getInstance()->addObserver(this, callfuncO_selector(Air::StopRelease), "StopAirRelease", nullptr);

	listenner->setEnabled(true); 
}

void Air::addParticle(Point pos,float lenght){
#define rand (((float)RandomProducer::create()->getRandom(-1000,1000))/1000.0)
#define PI acos(-1)
	log("rand = %lf",rand);
    Texture2D* texture = Director::getInstance()->getTextureCache()->addImage("air(big).png");
    auto newParticle =  AirParticle::create(this,texture,(_speed + _speedVar*rand)*Point(cos(rand*PI),sin(rand*PI)),4+rand*_sizeVar,_life+rand*_lifeVar,_startOpacity,_endOpacity);
    this->addChild(newParticle);
    newParticle->setPosition(pos);
	newParticle->setScale(0.45);
	//newParticle->setPosition(newParticle->getPosition()+ _posVar*rand*Point(cos(rand*PI),sin(rand*PI))); 
}
